'use strict'

import { app, protocol, BrowserWindow, globalShortcut, ipcMain, dialog, Notification } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import installExtension, { VUEJS3_DEVTOOLS } from 'electron-devtools-installer'
import path from 'path';
import fs from 'fs';
import { v4 } from 'uuid';
import chokidar from 'chokidar';
// 环境
const isDevelopment = process.env.NODE_ENV !== 'production'

// 主窗口id
let mainWinId: number | null = null;

// 路径监听
interface watchFileTeyps {
  type: "file" | "dir",
  filename: string,
  filepath: string,
  id: string,
  children?: watchFileTeyps[]
}
// 储存
let watchrDir: chokidar.FSWatcher | null = null;


// 错误弹窗
const errorDialog = (err: Error, text: string) => {
  if (mainWinId === null) return false;
  const mainWindow = BrowserWindow.fromId(mainWinId);
  mainWindow && dialog.showMessageBoxSync(mainWindow, {
    type: 'question',
    buttons: ['确认'],
    title: '错误提示',
    message: err.message || text,
  })
}


// 通信
const createSocket = () => {
  // 文件操作
  ipcMain.on('fileSendSocket', (event, message) => {
    message = JSON.parse(message);
    const { type } = message;
    switch (type) {
      case 'getDir':
        // 获取文件路径
        getDir(event, message);
        break;
      case 'getDirList':
        // 获取文件目录
        getDirList(event, message);
        break;
      case 'getFile':
        // 获取文件
        getFile(event, message);
        break;
      case 'setFile':
        // 重写文件
        setFile(event, message);
        break;
      case 'rnameFile':
        // 重命名文件夹或文件
        rnameFile(event, message);
        break;
      case 'createFile':
        // 创建文件夹或文件
        createFile(event, message);
        break;
      case 'deleteFile':
        // 删除文件夹或文件
        deleteFile(event, message);
        break;
      default:
        break;
    }
  })

  // 获取文件
  const getFile = (event: Electron.IpcMainEvent, message: any) => {
    const { type, data } = message;
    if (!data) return false;
    if (fs.existsSync(data)) {
      // 异步读取文件
      fs.readFile(data, 'utf8', (err, data) => {
        if (err) {
          errorDialog(err, '读取文件时出错');
        } else {
          event.sender.send('fileOnSocket', {
            type, data: {
              code: 200,
              value: data
            }
          })
        }
      });
    } else {
      event.sender.send('fileOnSocket', {
        type, data: {
          code: 404,
        }
      })
    }
  }
  // 重写文件
  const setFile = (event: Electron.IpcMainEvent, message: any) => {
    const { type, data } = message;
    if (!data) return false;
    if (fs.existsSync(data.filepath)) {
      // 异步写入文件
      fs.writeFile(data.filepath, data.value, 'utf8', (err) => {
        if (err) {
          errorDialog(err, '文件重写时出错');
        }
      });
    } else {
      event.sender.send('fileOnSocket', {
        type, data: {
          code: 404,
        }
      })
    }
  }

  // 获取文件目录
  const getDir = (event: Electron.IpcMainEvent, message: any) => {
    const { type, data } = message;
    if (mainWinId === null) return false;
    const mainWindow = BrowserWindow.fromId(mainWinId);
    mainWindow && dialog.showOpenDialog(mainWindow, {
      properties: ['openFile', 'openDirectory']
    }).then(result => {
      if (!result.canceled) {
        const selectedFolder = result.filePaths[0]
        event.sender.send('fileOnSocket', { type, data: selectedFolder })
      } else {
        event.sender.send('fileOnSocket', { type, data: '' })
      }
    }).catch(err => {
      dialog.showMessageBoxSync(mainWindow, {
        type: 'question',
        buttons: ['确认'],
        title: '错误提示',
        message: err.message || '打开文件夹对话框时发生错误',
      })
    })
  }

  // 获取文件路径下的目录
  const getDirList = (event: Electron.IpcMainEvent, message: any) => {

    const { type, data } = message;
    if (!data) return false;
    // 监听文件夹，返回目录列表
    const getFilesAndDirectoriesInfo = (filepath: string) => {
      const filename = path.basename(filepath);
      const isDirectory = fs.statSync(filepath).isDirectory();

      let resultItem: watchFileTeyps = {
        filename,
        id: v4(),
        type: isDirectory ? 'dir' : 'file',
        filepath,
      };
      // 文件夹处理
      if (isDirectory) {
        const files: watchFileTeyps[] = [];
        const items = fs.readdirSync(filepath);
        items.forEach((filename) => {
          const _filepath = path.join(filepath, filename);
          const res = getFilesAndDirectoriesInfo(_filepath);
          files.push(res);
        });
        // 赋值
        resultItem['children'] = files
      }

      return resultItem;
    }
    // 重新排序，且删除监听字段
    const dirfile = (data: watchFileTeyps) => {
      if (data.type === 'dir') {
        if (data.children) {
          const dir: watchFileTeyps[] = [], file: watchFileTeyps[] = [];
          data.children.forEach(item => {
            item = dirfile(item);
            if (item.type === 'dir') {
              dir.push(item)
            } else {
              file.push(item)
            }
          });
          data.children = dir.concat(file);
        }
      }
      return data;
    }
    const sendRes = () => {
      // 获取列表
      const watchFile = getFilesAndDirectoriesInfo(data);
      // 修改
      const res = dirfile(watchFile);
      event.sender.send('fileOnSocket', {
        type, data: res
      })

    }
    if (fs.existsSync(data)) {
      // 取消监听
      if (watchrDir !== null) {
        watchrDir.close();
        watchrDir = null;
      }
      // 添加监听
      watchrDir = chokidar.watch(data, {
        ignored: /(^|[\/\\])\../, // 忽略隐藏文件
        persistent: true, // 是否持续监听
      });
      watchrDir
        .on('add', (path: string) => {
          sendRes();
          console.log(`文件已添加: ${path}`)
        })
        .on('addDir', (path: string) => {
          sendRes();
          console.log(`文件夹已添加: ${path}`)
        })
        .on('change', (path: string) => console.log(`文件已修改: ${path}`))
        .on('unlink', (path: string) => {
          sendRes();
          console.log(`文件已删除: ${path}`)
        })
        .on('unlinkDir', (path: string) => {
          sendRes();
          console.log(`文件夹已删除: ${path}`)
        })
        .on('error', (error: string) => console.error(`发生错误: ${error}`))
        .on('ready', () => {
          sendRes();
          console.log('监视器已准备就绪')
        });
    } else {
      event.sender.send('fileOnSocket', { type, data: '' })
    }

  }
  // 重命名文件夹或文件
  const rnameFile = (event: Electron.IpcMainEvent, message: any) => {
    const { type, data } = message;
    const { filename } = data.value;
    const newpath = data.oldpath.replace(path.basename(data.oldpath), filename);
    // 重命名文件夹
    fs.rename(data.oldpath, newpath, (err) => {
      if (err) {
        errorDialog(err, '重命名失败')
        return false;
      }
    });
  }
  // 创建文件夹或文件
  const createFile = (event: Electron.IpcMainEvent, message: any) => {
    const { data } = message;
    const { filename, radio, type } = data.value;
    let willCreateFolder = `${data.oldpath}/${filename}`;
    if (radio !== 'dir') {
      willCreateFolder += '.' + radio
    }
    const notification = new Notification({ title: '错误提示', body: radio === 'dir' ? '文件夹已经存在' : '文件已存在' })
    // 验证文件夹是否存在
    if (!fs.existsSync(willCreateFolder)) {
      if (radio === 'dir') {
        // 文件夹不存在，可以创建
        fs.mkdir(willCreateFolder, { recursive: true }, (err) => {
          if (err) {
            errorDialog(err, '创建文件夹时出错')
          }
        });
      } else {
        // 文件不存在，可以创建
        fs.writeFile(willCreateFolder, '', (err) => {
          if (err) {
            errorDialog(err, '创建文件时出错')
          }
        });
      }
    } else {
      notification.show();
    }
  }
  // 删除文件夹或文件
  const deleteFile = (event: Electron.IpcMainEvent, message: any) => {
    const { type, data } = message;
    const isDirectory = fs.statSync(data.oldpath).isDirectory();
    if (isDirectory) {
      // 卸载对应目录监听
      fs.rmdir(data.oldpath, { recursive: true }, (err) => {
        if (err) {
          errorDialog(err, '删除文件夹时出错')
        }
      });
    } else {
      fs.unlink(data.oldpath, (err) => {
        if (err) {
          errorDialog(err, '删除文件时出错')
        }
      });
    }
  }
}

// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
  { scheme: 'app', privileges: { secure: true, standard: true } }
])

async function createWindow() {
  // Create the browser window.
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    minWidth: 800,
    minHeight: 600,
    backgroundColor: "#002b36",
    title: '麻雀',
    show: false,
    frame: false,
    // resizable: false,
    center: true,
    icon: path.resolve(__dirname, '../public/favicon.ico'),
    titleBarStyle: 'hidden',
    webPreferences: {

      nodeIntegration: true,
      enableRemoteModule: true,
      contextIsolation: true,
      preload: path.join(__dirname, 'preload.js'),

    }
  })


  // 注册快捷键
  if (isDevelopment) {
    const ret = globalShortcut.register('CommandOrControl+F12', () => {
      win.webContents.openDevTools({ mode: 'detach' });
    });

    if (!ret) {
      console.log('注册 F12 快捷键失败');
    }
  }

  if (process.env.WEBPACK_DEV_SERVER_URL) {
    // Load the url of the dev server if in development mode
    await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string)
    // if (!process.env.IS_TEST) win.webContents.openDevTools()
  } else {
    createProtocol('app')
    // Load the index.html when not in development
    win.loadURL('app://./index.html')
  }

  mainWinId = win.id;
  win.setMenu(null);
  win.show()
}

// Quit when all windows are closed.
app.on('window-all-closed', () => {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (BrowserWindow.getAllWindows().length === 0) createWindow()
})

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
  if (isDevelopment && !process.env.IS_TEST) {
    // Install Vue Devtools
    try {
      await installExtension(VUEJS3_DEVTOOLS)
    } catch (e) {
      // @ts-ignore
      console.error('Vue Devtools failed to install:', e.toString())
    }
  }
  createSocket();
  createWindow();
})

// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
  if (process.platform === 'win32') {
    process.on('message', (data) => {
      if (data === 'graceful-exit') {
        app.quit()
      }
    })
  } else {
    process.on('SIGTERM', () => {
      app.quit()
    })
  }
}

app.on('will-quit', () => {
  // 取消监听
  if (watchrDir !== null) {
    watchrDir.close();
    watchrDir = null;
  }
  // 卸载快捷键
  if (isDevelopment) globalShortcut.unregister('CommandOrControl+F12');
})

